#include "head.h"
#define LENGTH 4096 //每次内存复制的大小，必须是LENGTH的整数倍

int hash_file(char *hash, char *file_name){
    char buf[4096] = {0};
    char sha1sum[50] = {0};
    sprintf(buf,"sha1sum %s",file_name);
    FILE *pipe = popen(buf, "r");
    //FILE *pipe = popen("md5sum file.txt", "r");
    fread(sha1sum, 1, sizeof(sha1sum), pipe);
    printf("sha1sum: %s\n", sha1sum);
    strncpy(hash, sha1sum, 40);
    printf("hash: %s\n", hash);
    pclose(pipe);
    return 0;
}

int second_hash(char *rehash, char *file_name){
    char buf[4096] = {0};
    char sha512sum[200] = {0};
    sprintf(buf,"sha512sum %s",file_name);
    FILE *pipe = popen(buf, "r");
    //FILE *pipe = popen("md5sum file.txt", "r");
    fread(sha512sum, 1, sizeof(sha512sum), pipe);
    printf("sha512sum: %s\n", sha512sum);
    strncpy(rehash, sha512sum, 128);
    printf("rehash: %s\n", rehash);                                                             
    pclose(pipe);
    return 0;
}

int send_bigfile(int socketfd, char *file_name){
    char hash[1024] = {0};
    hash_file(hash, file_name);
    int fd = open(file_name, O_RDWR);
    ERROR_CHECK(fd, -1, "open");

    send(socketfd, hash, sizeof(hash), MSG_NOSIGNAL);

    int flag_exist = 0;
    recv(socketfd, &flag_exist, sizeof(int), MSG_WAITALL);

    if(flag_exist == 1){
        char rehash[200] = {0};
        second_hash(rehash, file_name);
        send(socketfd, rehash,sizeof(rehash), MSG_NOSIGNAL);
        int flag_same = 0;
        recv(socketfd, &flag_same, sizeof(int), MSG_WAITALL);    
        if(flag_same == 1){
            printf("存在相同文件，文件上传完毕\n");
            return 0;
        }
    }
    else if(flag_exist == 2){
        printf("文件完全相同，不需要上传\n");
        return 0;
    }
    

    printf("flag_exist = %d\n", flag_exist);
    printf("-------\n");
    //获取要上传到服务端的文件的大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);
    send(socketfd, &stat_file.st_size, sizeof(off_t), MSG_NOSIGNAL);

    printf("-------\n");
    //接收服务端现有文件的大小
    off_t real_size;
    recv(socketfd, &real_size, sizeof(off_t), MSG_WAITALL);
    printf("real size: %ld\n", real_size);

    //position用于记录当前文件指针的位置
    //因为position要作为mmap的offset参数，所以只能是LENGTH的整数倍
    int position = (real_size/LENGTH)*LENGTH;
    //printf("position: %d\n", position);

    //每次传输的长度
    int length = LENGTH;

    //发送文件
    while(1){
        //如果总文件的大小和当前传输到的位置(positionon)相差的值小于一次传输的长度，就把长度改为这个差值
        if(stat_file.st_size - position < LENGTH){
            length = stat_file.st_size -position;
        }
        //从position的位置映射length的长度到file_read
        char *file_read = (char *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, position);
        ERROR_CHECK(file_read, MAP_FAILED, "mmap");
        //传输内容的长度
        send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
        //printf("length = %d\n", length);
        //传输的内容
        send(socketfd, file_read, length, MSG_NOSIGNAL);
        munmap(file_read, length);
        //传输一次后，更新position的值
        position += LENGTH;
        //printf("position: %d\n", position);
        //如果position的值大于等于文件的总长度，说明position已经走到了文件的末尾
        //向服务端发送一个长度，值为0，表示传输结束
        if(position >= stat_file.st_size){
            length = 0;
            send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
            printf("文件传输完毕\n");
            break;
        }
    }
    close(fd);
    return 0;
}


int recv_bigfile(int socketfd, char *name){
    //根据服务端传来的信息判断要求的文件是否存在
    int flag;
    recv(socketfd, &flag, sizeof(int), MSG_WAITALL);
    if(flag == -1){
        printf("服务端文件丢失\n");
        return 1;
    }
    else if(flag == 0){ 
        printf("请求下载文件的记录在服务端不存在\n");
        return 2;
    }

    int fd = open(name, O_WRONLY|O_CREAT, 0664);

    //现有文件大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    //printf("file size: %ld\n", stat_file.st_size);
    //将现有文件大小发给服务端
    send(socketfd, &stat_file.st_size, sizeof(off_t), MSG_NOSIGNAL);
    //判断文件是否一致，一致则直接结束
    char hash[50] = {0};
    hash_file(hash ,name);
    send(socketfd, hash, sizeof(hash), MSG_NOSIGNAL);
    int flag_same;
    recv(socketfd, &flag_same, sizeof(int), MSG_WAITALL);
    if(flag_same == 1){
        printf("文件完全一致，不需要下载\n");
        return 0;
    }
    
    //获取断点
    off_t position = (stat_file.st_size/LENGTH)*LENGTH;
    //printf("position: %ld\n", position);
    //改变文件指针的指向到断点
    int ret_lseek = lseek(fd, position, SEEK_SET);
    ERROR_CHECK(ret_lseek, -1, "lseek");

    //接收文件
    while(1){
        //sleep(1);
        int buf_len;
        char buf[LENGTH+1] = {0};
        recv(socketfd, &buf_len, sizeof(int), MSG_WAITALL);
        if(buf_len == 0){
            printf("文件下载完毕\n");
            break;
        }
        //printf("buf_len: %d\n", buf_len);
        recv(socketfd, buf, buf_len, MSG_WAITALL);
        //printf("buf: %s\n", buf);
        write(fd, buf, buf_len);
        //printf("buf_len: %d\n", buf_len);
        if(buf_len == 0){
            printf("文件下载完毕\n");
            break;
        }
    }
    close(fd);
    return 0;

}

